package io.hellsinger.vortex.util

import io.hellsinger.filesystem.attribute.PathType
import io.hellsinger.filesystem.linux.attribute.attributes
import io.hellsinger.filesystem.linux.directory.asLinuxDirectory
import io.hellsinger.filesystem.linux.directory.size
import io.hellsinger.filesystem.linux.file.asLinuxFile
import io.hellsinger.theme.vortex.VortexSettings
import io.hellsinger.vortex.bit.Bits.Companion.hasFlag
import io.hellsinger.vortex.data.model.PathItem
import io.hellsinger.vortex.data.model.parseThemeType
import io.hellsinger.vortex.util.DefaultPathItemDescriptor.Companion.DIR_COUNT
import io.hellsinger.vortex.util.DefaultPathItemDescriptor.Companion.FILE_LMT
import io.hellsinger.vortex.util.DefaultPathItemDescriptor.Companion.FILE_MIMETYPE
import io.hellsinger.vortex.util.DefaultPathItemDescriptor.Companion.FILE_SIZE
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.withContext

interface PathItemDescriptor {
    suspend fun describe(
        item: PathItem,
        options: Int = DIR_COUNT or FILE_MIMETYPE or FILE_SIZE or FILE_LMT,
    ): String
}

class DefaultPathItemDescriptor : PathItemDescriptor {
    private val builder = StringBuilder()

    override suspend fun describe(
        item: PathItem,
        options: Int,
    ): String =
        withContext(Dispatchers.IO) {
            builder.clear()

            when (item.type) {
                PathType.Directory -> {
                    val directory = item.asLinuxDirectory()
                    val attrs = item.attributes
                    if (options hasFlag DIR_COUNT) wrap(builder, dirCount(directory.count()))
                    if (options hasFlag DIR_SIZE) wrap(builder, directory.size().toString())
                    if (options hasFlag DIR_HIDDEN) wrap(builder, item.isHidden)
                    if (options hasFlag DIR_ABSOLUTE) wrap(builder, item.isPathAbsolute)
                    if (options hasFlag DIR_LMT) wrap(builder, attrs.lastModifiedTime.toString())
                    if (options hasFlag DIR_LAT) wrap(builder, attrs.lastAccessTime.toString())
                    if (options hasFlag DIR_CT) wrap(builder, attrs.creationTime.toString())
                    if (options hasFlag DIR_PERM) wrap(builder, attrs.permission)
                }

                PathType.File -> {
                    val file = item.asLinuxFile()
                    val attrs = item.attributes
                    if (options hasFlag FILE_SIZE) {
                        wrap(
                            builder,
                            file.size.format("#.##").toString(),
                        )
                    }
                    if (options hasFlag FILE_MIMETYPE) {
                        item.mime.parseThemeType()?.let { wrap(builder, it) }
                    }

                    if (options hasFlag FILE_LMT) {
                        wrap(
                            builder,
                            attrs.lastModifiedTime.toString(),
                        )
                    }
                    if (options hasFlag FILE_LAT) {
                        wrap(
                            builder,
                            attrs.lastAccessTime.toString(),
                        )
                    }
                    if (options hasFlag FILE_CT) wrap(builder, attrs.creationTime.toString())
                    if (options hasFlag FILE_HIDDEN) wrap(builder, item.isHidden)
                    if (options hasFlag FILE_ABSOLUTE) wrap(builder, item.isPathAbsolute)
                    if (options hasFlag FILE_PERM) wrap(builder, attrs.permission)
                }
            }

            builder.toString()
        }

    private fun wrap(
        builder: StringBuilder,
        data: String,
    ) {
        if (builder.isNotEmpty()) {
            builder.append(VortexSettings.texts { key_storage_list_item_description_separator })
        }
        builder.append(data)
    }

    private fun wrap(
        builder: StringBuilder,
        data: Boolean,
    ) {
        if (builder.isNotEmpty()) {
            builder.append(VortexSettings.texts { key_storage_list_item_description_separator })
        }
        builder.append(data)
    }

    companion object {
        const val DIR_COUNT = 1 shl 0
        const val DIR_SIZE = 1 shl 1
        const val DIR_LMT = 1 shl 2
        const val DIR_LAT = 1 shl 3
        const val DIR_CT = 1 shl 4
        const val DIR_HIDDEN = 1 shl 5
        const val DIR_ABSOLUTE = 1 shl 6

        const val FILE_SIZE = 1 shl 7
        const val FILE_LMT = 1 shl 8
        const val FILE_LAT = 1 shl 9
        const val FILE_CT = 1 shl 10
        const val FILE_HIDDEN = 1 shl 11
        const val FILE_ABSOLUTE = 1 shl 12
        const val FILE_MIMETYPE = 1 shl 13

        const val DIR_PERM = 1 shl 14
        const val FILE_PERM = 1 shl 15
    }
}

suspend fun PathItem.launchLoadingDescription(
    descriptor: PathItemDescriptor,
    options: Int = DIR_COUNT or FILE_MIMETYPE or FILE_SIZE or FILE_LMT,
) = coroutineScope {
    description =
        async {
            descriptor.describe(item = this@launchLoadingDescription, options = options)
        }
    return@coroutineScope this@launchLoadingDescription
}
